Webpack- Single-file components(.vue)

在 Webpack 4 之後的版本,將 Webpack 及 Webpack-cli 拆開了,因此在載用 webpack 時,也要安裝 webpack-cli。

在搭建(scaffold)一個專案時,最推薦使用框架的命令行介面(command lin interface, CLI),提供一個入口檔案及配置文件,這個鷹架(scaffolding)會透過開發建置流程,最終將檔案打包出一個生產版本。

一般使用 Vue.js 僅需要用<script>標籤引入 CDN,不需要開發建置過程,專案中即可使用 Vue.js。但是,如果要使用 Vue single-page components(SFCs),就一定要使用開發建置工具,將.vue檔案轉換為瀏覽器可以解讀的 HTML、CSS、JavaScript。

Vue-cli 提供預設好的開發建置流程,但是,如果想要自己定義,則可以使用 Webpack 來搭建(scaffold)專案。

Webpack

Webpack 是一個模組打包工具,他可以將多個檔案中的程式碼合併成一個。在 Webpack 出現之前,必須要透過<script>標籤將多個 JavaScript 引入到一個檔案中。

此外,Webpack 也透過 loader 和 plugin 來轉換程式碼,例如將 ES6+ 轉換為 ES5,或是將 SCSS 轉為 CSS。

用 SPC 實作一個 Vue 專案

Step 1.專案結構建置

最基本的專案應當要有一個 HTML、一個 JavaScript、一個 Vue 檔案(.vue),通常會將這些檔案放置在 src 的資料夾中,用以區分這些是我們寫的程式碼,避免跟 Webpack 打包建置後的檔案搞混。

我們會使用到 Webpack ,因此需要一個 Webpack 的配置檔案,通常命名為 webpack.config.js

此外,我們會需要 Babel 將 ES6 編譯為 ES5,Babel 本身也是 Webpack 的附加插件,因此需要一個 Babel 的配置檔案,命名為.babelrc.js

所以先依照上述說明建置如下專案結構:

1
2
3
4
5
6
src(forder)
|_myApp2.vue
|_index.html
|_main.js
webpack.config.js
.babelrc.js

接著,我們會使用 NPM 來做依賴(dependencies)管理,需要透過指令將專案初始化,並跟著 terminal 視窗中的提示問題完成創建:

1
npm init

初始化之後,會發現專案中自動多出了一個 package.json 檔案。所以目前應該會看起來像這樣:

Step 2.安裝套件依賴

以下是幾個我們目前會使用到到的套件依賴:

  • vue: JS 前端框架
  • vue-loader & vue-template-compiler:將 vue 檔案轉換為 JavsSript
  • webpack:轉換並打包程式碼
  • webpack-cli:用以執行 Webpack 命令
  • webpack-dev-server:雖然在這裡示範的小專案不會用到 HTTP request,但使我們會需要開發伺服器來服務這個專案
  • babel-loader:將 ES6 語法轉換為 ES5(需要搭配下面兩個插件依賴使用)
  • @babel/core & @babel/preset: Babel-loader 本身無法作用,必須搭配這兩個插件才能將 ES6 語法轉換為 ES5
  • css-loader:會將.vue檔案中的 CSS 或是任何引入的 CSS,載入到 JavaScript 檔案中,並且解析路徑。
  • vue-style-loader:從 css-loader 取得 CSS 之後,會再透過 vue-style-loader 引入到 HTML 中,會在 HTML 的 head 中,自動生成出一個<style> 標籤。簡單來說,css-loader 是單純將 css 作載入 js;style-loader 則是作解析的動作,讓頁面能讀懂 css,所以它們倆合體就可以達成目的了。
  • html-webpack-plugin:將 index.html 還有打包後的 js 引入到<head>,並且複製到 dist 資料夾中。
  • rimraf:讓我們可以從命令行中刪除檔案

接著就在命令行中輸入指令,開始安裝依賴:分開發依賴(指令添加-S)、生產依賴(指令添加-D)

1
npm install vue -S
1
npm install vue-loader vue-template-compiler webpack webpack-cli webpack-dev-server babel-loader @babel/core @babel/preset-env css-loader vue-style-loader html-webpack-plugin rimraf -D

安裝完畢之後,可以在 package.json 中查看,另外,專案中也會自動產生一個 node_modules 的資料夾,目前專案看起來會像這樣:

Step 3.檔案創建

myApp.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div id="app">
{{ message }}
</div>
</template>

<script>
export default {
data() {
return {
message: 'Hello World',
};
},
};
</script>

<style>
#app {
font-size: 18px;
font-family: 'Roboto', sans-serif;
color: blue;
}
</style>

index.html

1
2
3
4
5
6
7
8
<html>
<head>
<title>Vue Hello World</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

main.js

1
2
3
4
5
6
7
import Vue from 'vue';
import myApp from './myApp.vue';

new Vue({
el: '#app',
render: (h) => h(myApp),
});

解釋一下第 6 行程式碼render: h => h(myApp)的縮寫流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 完整原始程式碼
render: function (createElement) {
return createElement(myApp);
};
// 簡化
render(createElement){
return createElement(myApp);
};
// 再簡化
render(h){
return h(myApp);
};
// 最終簡化
render(h) => h(myApp);

// createElement函式是用來生成HTML DOM元素
// 也就是生成HTML結構中的 script 腳笨
// 所以用Hyperscript取h作為程式碼中createElement的簡寫。

babelrc.js

1
2
3
module.exports = {
presets: ['@babel/preset-env'],
};

Step 4.配置 Webpack

到目前為止,Webpack 需要訪問的檔案都備齊了,接著剩下最後兩件事情:告訴 Webpack 做什麼,並且執行 Webpack。

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
entry: './src/main.js',
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' },
{ test: /\.vue$/, use: 'vue-loader' },
{ test: /\.css$/, use: ['vue-style-loader', 'css-loader'] },
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new VueLoaderPlugin(),
],
};
  • 第 1、2 行,import 下面會用到的 plugin。注意!loader 不需要 import!(不過在這個案例中,第 9 行的 vue-loader 會需要用到第 17 行的 plugin 才能運作,但是像 babel-loader 就不需要。)
  • 第 4 行,將配置(configuration)以物件的型態 export,這也是執行 Webpack 命令的入口。
  • 第 5 行,這是入口模組(在 Webpack 中,每個檔案都可視為一個模組),Webpack 需要一個起始點,所以 Webpack 會從main.js開始查找去梳理所有的程式碼。
  • 第 7 行,用rules陣列來告訴 Webpack 在梳理程式碼時要遵循的規則。
  • 第 8 行,這條規則指示 Webpack 當遇到以.js結尾的檔案時,要使用babel-loader去轉換程式碼。
  • 第 9 行,這條規則指示 Webpack 當遇到以.vue結尾的檔案時,要使用vue-loader(需要搭配地 17 行的插件)去轉緩程式碼。
  • 第 10 行,這條規則是指示 Webpack:先用css-loader.vue檔案中拿出 CSS,再用vue-style-loader轉以<style>tag 的形式放進 HTML 中。(有些檔案會需要兩個 loader 去執行,在 Webpack 中的處理流程有點違反直覺,通常都是從左到右,但是 Webpack 是從右到左。)
  • 第 13 行,用plugins陣列,放入我們需要用到的 plugin
  • 第 14 行,HtmlWebpackPlugin這個 plugin 會獲取index.html檔案的位置,並且將打包後的 JavaScript 檔案以<script>標籤放入其中。此外,這個 plugin 也會複製 HTML 檔案到 build 後的資料夾中。
  • 第 17 行,VueLoaderPlugin這個 plugin 會搭配第 9 行的vue-loader解析.vue檔案。

HtmlWebpackPlugin 獲取 index.html 文件的位置,並通過腳本標記將捆綁的 JavaScript 文件添加到其中。

Step 5.執行 Webpack

上述所有的配置都完成之後,最後只要設定好package.json檔,就可以執行 Webpack 了。在理想的情況下,當我們修改內容,會希望瀏覽器自動更新,因此我們需要webpack-dev-server

webpack-dev-server的功能簡而言之,就是當檔案有修改儲存,重新 bundle 之後,開發人員不需要按下重整按鈕,就會通知瀏覽器執行畫面重整。但是要注意,webpack-dev-server僅適用於開發階段,它產生出的bundle.js並不會出現在我們專案中的 dist 資料夾,而是暫存在記憶體中,所以如果要上正式發布環境,請參考 Step7。

package.json

  • 在第 7 行scripts的地方刪掉原先預設的test,改以serve取代之。(命名為 serve 並非一定,可以自己隨心改變)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"name": "vue-single-file-components",
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"scripts": {
"serve": "webpack-dev-server --mode development"
},
"author": "",
"license": "ISC",
"dependencies": {
"vue": "^2.6.10"
},
"devDependencies": {
"@babel/core": "^7.4.5",
"@babel/preset-env": "^7.4.5",
"babel-loader": "^8.0.6",
"css-loader": "^3.0.0",
"html-webpack-plugin": "^3.2.0",
"rimraf": "^2.6.3",
"vue-loader": "^15.7.0",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.34.0",
"webpack-cli": "^3.3.4",
"webpack-dev-server": "^3.7.1"
}
}

接著就可以在 terminal 或 command line 裡面輸入指令啟用 Webpack:

1
npm run serve

或是

1
webpack-dev-serve --mode development

如果一切正確無誤,會看到「Compiled successfully」的字樣:

接著使用瀏覽器打開http://localhost:8080,可以看到畫面正確顯示:

Step 6.熱加載

有個狀況是,當我們在瀏覽器輸入資料後,又編輯檔案並存檔,此時瀏覽器中會重新刷新,剛剛輸入的資料都消失,所以我們需要使用 Webpack 內建的 Hot Module Replacement。

先修改myApp.vue模擬這個狀況:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<div id="myApp">
<input type="text" v-model="message" placeholder='Enter here'></input>
<h1>{{ message }}
</h1>
<h3>this is h4</h3>
</div>
</template>

<script>
export default {
data() {
return {
message: 'Hello',
};
},
};
</script>

<style>
#myApp {
font-size: 18px;
color: brown;
background-color: antiquewhite;
}
</style>

其實截至目前為止,在配置文件中我們都尚未引入 Webpack 物件,所以我現在們要來引入它,這樣我們才能 access 它的 plugin。

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const webpack = require('webpack');

module.exports = {
entry: './src/main.js',
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' },
{ test: /\.vue$/, use: 'vue-loader' },
{ test: /\.css$/, use: ['vue-style-loader', 'css-loader'] },
],
},
devServer: {
open: true,
hor: true,
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new VueLoaderPlugin(),
new webpack.HotModuleReplacementPlugin(),
],
};
  • 第 3 行,引入 webpack
  • 第 23 行,因為我們在第 3 行引入了 webpack,所以可以使用webpack.HotModuleReplacementPlugin這個插件達到熱加載的功能
  • 第 14 行,sevServer 這是額外丟給 webpack 的 option。
  • 第 15 行,設定hottrue
  • 第 16 行,設定opentrue,當我們執行 nom run serve 時,會自動打開瀏覽器視窗

接著輸入執行指令

1
npm run serve

可以看到瀏覽器視窗自動打開了,且 console 的地方可以看到「Hot Module Replacement enabled」熱加載的功能已被成功啟用,此時編輯檔案中的內容再儲存,瀏覽器並不需要重新刷新,內容就會相對被更新。

Step 7.打包建置專案

前面 Step 5.提到在開發階段可以使用webpack-dev-server讓專案打包後可以在瀏覽器上被檢視,可是 bundle 後的檔案僅暫存於記憶體中,如果需要將專案打包壓縮後發布,則還是需要改用 webpack 指令。

所以進入 package.json 檔案中,在 scripts 中我們要新增幾個指令:

1
2
3
4
"scripts": {
"clean": "rimraf dist",
"build": "npm run clean && webpack --mode production"
},

因為每次執行 webpack 時,都會創造出 dist 資料夾以及其中的檔案,如果每次都要手動刪除 dist 資料夾在執行很麻煩,因此我們這邊使用 rimraf 的指令設定。

  • 第 2 行,前面專案中已經安裝 rimraf 依賴,在這邊可以使用它的指令,因此當我們在命令列輸入 npm run clean ,會獨立執行刪除的動作,rimraf dist告訴命令列刪除 dist
  • 第 3 行,這裡實際上有兩個指令用 && 分隔出來,會先執行npm run clean接著再執行webpack --mode production,是告訴 Webpack:先執行刪除動作,接著跑 webpack 建置(production 模式)動作

完整的package.json會長得像這樣:

接著,輸入建置指令:

1
npm run build

參考 terminal 中的提示,可以看到先執行了 rimraf dist 動作,接著成功完成建置打包的動作。

在專案中可以看到左邊專案結構自動生成了一個 dist 資料夾,裡面有打包後的 js 檔案,以及從 src 中複製過來的 html 檔案。

因為我們的 html 中沒有 HTTP request 動作,所以可以直接用瀏覽器打開 dist 中的 index.html。如果專案中有 HTTP request ,則需要將專案放到 server 環境中才能正常運作。

用瀏覽器打開index.html,並且檢視開發者工具,可以看到 element 的地方多出了一些內容,這些內容去比對index.html檔案是沒有的,比如說 <style>tag,因為這些內容是在生成過程中透過 JavaScript 動態放入的。

參考資料

© 2020 Leah's Blog All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero